#include <unistd.h>
#include <sys/stat.h>
#include <setjmp.h>
#include <sys/time.h>
#include <assert.h>
#include <time.h>
#include "syscall.h"
#include <stdint.h>

// helper macros
#define _concat(x, y) x ## y
#define concat(x, y) _concat(x, y)
#define _args(n, list) concat(_arg, n) list
#define _arg0(a0, ...) a0
#define _arg1(a0, a1, ...) a1
#define _arg2(a0, a1, a2, ...) a2
#define _arg3(a0, a1, a2, a3, ...) a3
#define _arg4(a0, a1, a2, a3, a4, ...) a4
#define _arg5(a0, a1, a2, a3, a4, a5, ...) a5

// extract an argument from the macro array
#define SYSCALL  _args(0, ARGS_ARRAY)
#define GPR1 _args(1, ARGS_ARRAY)
#define GPR2 _args(2, ARGS_ARRAY)
#define GPR3 _args(3, ARGS_ARRAY)
#define GPR4 _args(4, ARGS_ARRAY)
#define GPRx _args(5, ARGS_ARRAY)

// ISA-depedent definitions
#if defined(__ISA_X86__)
# define ARGS_ARRAY ("int $0x80", "eax", "ebx", "ecx", "edx", "eax")
#elif defined(__ISA_MIPS32__)
# define ARGS_ARRAY ("syscall", "v0", "a0", "a1", "a2", "v0")
#elif defined(__ISA_RISCV32__) || defined(__ISA_RISCV64__)
# define ARGS_ARRAY ("ecall", "a7", "a0", "a1", "a2", "a0")
#elif defined(__ISA_AM_NATIVE__)
# define ARGS_ARRAY ("call *0x100000", "rdi", "rsi", "rdx", "rcx", "rax")
#elif defined(__ISA_X86_64__)
# define ARGS_ARRAY ("int $0x80", "rdi", "rsi", "rdx", "rcx", "rax")
#else
#error _syscall_ is not implemented
#endif

/**
 * 对于 RISC-V, 函数 _syscall_ 会被翻译成如下的汇编: 
 *    mv	a7,a0
 *    mv	a0,a1
 *    mv	a1,a2
 *    mv	a2,a3
 *    ecall
 *    ret
 */
intptr_t _syscall_(intptr_t type, intptr_t a0, intptr_t a1, intptr_t a2) {
    /**
    *  [ register intptr_t _gpr1 asm (GPR1) = type ]
    *  展开: register intptr_t _gpr1 asm ("a7") = type;
    *  含义: 使用寄存器 a7 来存储 intptr_t 类型的变量 _gpr1, 即 
    *        这个定义声明了一个指针，并且指明了它的存储位置. 
    *  register 表示"变量"放在"机器的寄存器"
    */
    register intptr_t _gpr1 asm (GPR1) = type;
    register intptr_t _gpr2 asm (GPR2) = a0;
    register intptr_t _gpr3 asm (GPR3) = a1;
    register intptr_t _gpr4 asm (GPR4) = a2;
    register intptr_t ret asm (GPRx); // GPRx 代表存储返回值的寄存器, 在 RISC-V 中是寄存器 a0
    /**
    * asm volatile ( "ecall"
    *               : "=r" (ret)
    *               : "r"(_gpr1), "r"(_gpr2), "r"(_gpr3), "r"(_gpr4)
    *              );
    */
    asm volatile (SYSCALL : "=r" (ret) : "r"(_gpr1), "r"(_gpr2), "r"(_gpr3), "r"(_gpr4));
    return ret;
}

void _exit(int status) {
    _syscall_(SYS_exit, status, 0, 0);
    while (1); // 不可能到此
}

int _open(const char *path, int flags, mode_t mode) {
    return _syscall_(SYS_open, (intptr_t)path, flags, (intptr_t)mode);
}

int _read(int fd, void *buf, size_t count) {
    return _syscall_(SYS_read, fd, (intptr_t)buf, count);
}

int _write(int fd, void *buf, size_t count) {
    return _syscall_(SYS_write, fd, (intptr_t)buf, count);
}

int _close(int fd) {
    return _syscall_(SYS_close, fd, 0, 0);
}

off_t _lseek(int fd, off_t offset, int whence) {
    return _syscall_(SYS_lseek, fd, offset, whence);
}

/**
 * _sbrk()通过记录的方式来对用户程序的program break位置进行管理, 其工作方式如下:
 * 1. program break一开始的位置位于_end
 * 2. 被调用时, 根据记录的program break位置和参数increment, 计算出新program break
 * 3. 通过SYS_brk系统调用来让操作系统设置新program break
 * 4. 若SYS_brk系统调用成功, 该系统调用会返回0, 此时更新之前记录的program break的位置, 并将旧program break的位置作为_sbrk()的返回值返回
 * 5. 若该系统调用失败, _sbrk()会返回-1
 * 上述代码是在用户层的库函数中实现的, 我们还需要在Nanos-lite中实现SYS_brk的功能. 
 * 由于目前Nanos-lite还是一个单任务操作系统, 空闲的内存都可以让用户程序自由使用, 
 * 因此我们只需要让SYS_brk系统调用总是返回0即可, 表示堆区大小的调整总是成功.
 */
void * program_break = NULL;
void *_sbrk(intptr_t increment) {
    // the first address past the end of the uninitialized data segment (also known as the BSS segment)
    extern char end;
    if (program_break == NULL) {
        program_break = &end;
    }
    void* old_break = program_break;
    // 若 SYS_brk 系统调用成功, 该系统调用会返回0; 目前 nanos-lite 只会返回 0
    intptr_t ret = _syscall_(SYS_brk, (uintptr_t)program_break, increment, 0);
    assert(ret == 0);
    if (ret == 0) {
        program_break += increment;
        return old_break;
    }
    return (void *)-1;
}

int _gettimeofday(struct timeval *tv, struct timezone *tz) {
    return _syscall_(SYS_gettimeofday, (uintptr_t)tv, (uintptr_t)tz, 0);
}

/**
 * 上层 app 调用 int execve (const char *name, char *const argv[], char *const env[]) [navy-apps/libs/libc/src/syscalls/sysexecve.c]
 * 其调用 int _execve_r (struct _reent *ptr, const char *name, char *const argv[], char *const env[])  [navy-apps/libs/libc/src/syscalls/execr.c]
 * 其再调用 _execve (const char *fname, char * const argv[], char *const envp[]) 
 */

int _execve(const char *fname, char * const argv[], char *const envp[]) {
    return _syscall_(SYS_execve, (uintptr_t)fname, (uintptr_t)argv, (uintptr_t)envp);
}

// Syscalls below are not used in Nanos-lite.
// But to pass linking, they are defined as dummy functions.

int _fstat(int fd, struct stat *buf) {
    return -1;
}

int _stat(const char *fname, struct stat *buf) {
    assert(0);
    return -1;
}

int _kill(int pid, int sig) {
    _exit(-SYS_kill);
    return -1;
}

pid_t _getpid() {
    _exit(-SYS_getpid);
    return 1;
}

pid_t _fork() {
    assert(0);
    return -1;
}

pid_t vfork() {
    assert(0);
    return -1;
}

int _link(const char *d, const char *n) {
    assert(0);
    return -1;
}

int _unlink(const char *n) {
    assert(0);
    return -1;
}

pid_t _wait(int *status) {
    assert(0);
    return -1;
}

clock_t _times(void *buf) {
    assert(0);
    return 0;
}

int pipe(int pipefd[2]) {
    assert(0);
    return -1;
}

int dup(int oldfd) {
    assert(0);
    return -1;
}

int dup2(int oldfd, int newfd) {
    return -1;
}

unsigned int sleep(unsigned int seconds) {
    assert(0);
    return -1;
}

ssize_t readlink(const char *pathname, char *buf, size_t bufsiz) {
    assert(0);
    return -1;
}

int symlink(const char *target, const char *linkpath) {
    assert(0);
    return -1;
}

int ioctl(int fd, unsigned long request, ...) {
    return -1;
}
